package org.orange.spring.mvc.simulate.coolingme.springframework.context;



import org.orange.spring.mvc.simulate.coolingme.springframework.annotation.MeAutowired;
import org.orange.spring.mvc.simulate.coolingme.springframework.annotation.MeController;
import org.orange.spring.mvc.simulate.coolingme.springframework.annotation.MeService;
import org.orange.spring.mvc.simulate.coolingme.springframework.beans.MeBeanWrapper;
import org.orange.spring.mvc.simulate.coolingme.springframework.beans.config.MeBeanDefinition;
import org.orange.spring.mvc.simulate.coolingme.springframework.context.support.MeBeanDefinitionReader;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 简单理解为工厂类
 * 完成Bean的创建和DI
 *
 * @author wangyue
 * @date 2020/11/10 15:24
 */
public class MeApplicationContext {

    /**
     * 配置文件读取对象
     */
    private MeBeanDefinitionReader reader;

    /**
     * 存储注册信息的容器
     * "demoServiceImpl" -> {MeBeanDefinition@5122} "MeBeanDefinition{factoryBeanName='demoServiceImpl', beanClassName='com.coolingme.demo.service.impl.DemoServiceImpl'}"
     * "com.coolingme.demo.service.DemoService" -> {MeBeanDefinition@5123} "MeBeanDefinition{factoryBeanName='com.coolingme.demo.service.DemoService', beanClassName='com.coolingme.demo.service.impl.DemoServiceImpl'}"
     * //"com.coolingme.demo.service.impl.DemoServiceImpl" -> {MeBeanDefinition@5123} "MeBeanDefinition{factoryBeanName='com.coolingme.demo.service.DemoService', beanClassName='com.coolingme.demo.service.impl.DemoServiceImpl'}"
     * //"com.coolingme.demo.controller.DemoController" -> {MeBeanDefinition@5121} "MeBeanDefinition{factoryBeanName='demoController', beanClassName='com.coolingme.demo.controller.DemoController'}"
     * "demoController" -> {MeBeanDefinition@5121} "MeBeanDefinition{factoryBeanName='demoController', beanClassName='com.coolingme.demo.controller.DemoController'}"
     */
    private Map<String, MeBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 单例的IOC容器缓存
     * "com.coolingme.demo.service.impl.DemoServiceImpl" -> {DemoServiceImpl@5132}
     * "com.coolingme.demo.controller.DemoController" -> {DemoController@5134}
     */
    private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();

    /**
     * 通用的IOC容器缓存
     * "demoServiceImpl" -> {MeBeanWrapper@5143}
     * //"com.coolingme.demo.service.DemoService" -> {MeBeanWrapper@5145}
     * "com.coolingme.demo.service.impl.DemoServiceImpl" -> {MeBeanWrapper@5146}
     * //"com.coolingme.demo.controller.DemoController" -> {MeBeanWrapper@5147}
     * "demoController" -> {MeBeanWrapper@5149}
     */
    private Map<String, MeBeanWrapper> factoryBeanInstancesCache = new ConcurrentHashMap<>();


    public MeApplicationContext(String... configLocations) {
        try {
            // 1、加载配置文件
            reader = new MeBeanDefinitionReader(configLocations);

            // 2、解析配置文件，封装成BeanDefinition
            List<MeBeanDefinition> beanDefinitions = reader.loadBeanDefinition();
            System.out.println("beanDefinitions");
            for (MeBeanDefinition beanDefinition : beanDefinitions) {
                System.out.println(beanDefinition);
            }

            // 3、把BeanDefinition缓存起来
            doRegisterBeanDefinition(beanDefinitions);

            // 注入Bean对象
            doAutowired();

            System.out.println("ApplicationContext is init.");

        } catch (Exception e) {
            System.err.println("ApplicationContext init fail.");
            e.printStackTrace();
        }
    }


    /**
     * 缓存配置文件信息
     *
     * @param beanDefinitions 配置文件信息
     */
    private void doRegisterBeanDefinition(List<MeBeanDefinition> beanDefinitions) throws Exception {
        for (MeBeanDefinition beanDefinition : beanDefinitions) {
            // 存在重复的key，直接抛异常
            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The is" + beanDefinition.getFactoryBeanName() + " exist");
            }
            // key不同，对象是同一个
//            beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    /**
     * 注入Bean对象
     * 延时加载时被getBean()触发
     */
    private void doAutowired() {
        for (Map.Entry<String, MeBeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }

    /**
     * 获取Bean对象
     * 依赖注入，从这里开始，通过读取 BeanDefinition 中的信息
     * 然后，通过反射机制创建一个实例并返回
     * Spring做法是，不会把最原始的对象放出去，会用一个 Beanwrapper来进行一次包装
     * 装饰器模式∶
     * 1、保留原来的0OP关系
     * 2、我需要对它进行扩展，增强（为了以后 AOP打基础）
     *
     * @param beanName bean名称
     * @return Bean对象
     */
    public Object getBean(String beanName) {
        // 1、获取bean配置信息
        MeBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);

        // 2、实例化对象
        Object instance = instantiateBean(beanName, beanDefinition);

        // 3、把对象封装到BeanWrapper中
        MeBeanWrapper beanWrapper = new MeBeanWrapper(instance);

        // 4、把BeanWrapper存到IoC容器中
        this.factoryBeanInstancesCache.put(beanName, beanWrapper);

        //5、执行依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);

        return beanWrapper.getWrappedInstance();
    }


    /**
     * 获取Bean对象
     *
     * @param beanClazz bean的类对象
     * @return Bean对象
     */
    public Object getBean(Class<?> beanClazz) {
        return this.getBean(beanClazz.getName());
    }

    /**
     * 实例化对象，放入IoC容器中
     *
     * @param beanName       bean名称
     * @param beanDefinition bean的配置信息
     * @return bean的实例
     */
    private Object instantiateBean(String beanName, MeBeanDefinition beanDefinition) {
        // 1、拿到要实例化的对象的类名
        String className = beanDefinition.getBeanClassName();

        // 2、反射实例化，得到一个对象
        Object instance = null;
        try {
            // 假设默认就是单例，暂时不考虑细节，先把主线拉通
            if (this.factoryBeanObjectCache.containsKey(className)) {
                instance = this.factoryBeanObjectCache.get(className);
            } else {
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();
                this.factoryBeanObjectCache.put(className, instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 执行依赖注入
     *
     * @param beanName       bean名称
     * @param beanDefinition bean的配置信息
     * @param beanWrapper    Bean的包装
     */
    private void populateBean(String beanName, MeBeanDefinition beanDefinition, MeBeanWrapper beanWrapper) {
        //可能涉及到循环依赖？
        //A{ B b}
        //B{ A b}
        //用两个缓存，循环两次
        //1、把第一次读取结果为空的BeanDefinition存到第一个缓存
        //2、等第一次循环之后，第二次循环再检查第一次的缓存，再进行赋值

        Object instance = beanWrapper.getWrappedInstance();
        Class<?> clazz = beanWrapper.getWrappedClass();

        // 只有加了注解才注入
        if (!(clazz.isAnnotationPresent(MeController.class) || clazz.isAnnotationPresent(MeService.class))) {
            return;
        }

        //把所有的包括private/protected/default/public 修饰字段都取出来
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 没有使用MeAutowired注释就不注入了
            if (!field.isAnnotationPresent(MeAutowired.class)) {
                continue;
            }

            MeAutowired meAutowired = field.getAnnotation(MeAutowired.class);
            String autowiredBeanName = meAutowired.value().trim();

            // 如果没有定义beanName，就默认根据类型注入
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }

            // 暴力访问
            field.setAccessible(true);

            // 反射赋值
            try {
                field.set(instance, this.factoryBeanInstancesCache.get(autowiredBeanName).getWrappedInstance());
                System.out.println("DI " + beanName + " => " + field.getName() + " : " + this.factoryBeanInstancesCache.get(autowiredBeanName).getWrappedInstance());
            } catch (Exception e) {
                System.err.println("依赖注入失败");
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取bean配置信息的数量
     *
     * @return bean配置信息的数量
     */
    public Integer getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    /**
     * 获取beanDefinitionMap的key数组
     *
     * @return beanDefinitionMap的key数组
     */
    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }
}
